#include "StdAfx.h"
#include "SamsungFreeCodecFilterConnection.h"

SamsungFreeCodecFilterConnection::SamsungFreeCodecFilterConnection(void)
{
}

SamsungFreeCodecFilterConnection::~SamsungFreeCodecFilterConnection(void)
{
}

DEFINE_GUID(CLSID_MyFreeSync,
	0xfd501041, 0x8ebe, 0x11ce, 0x81, 0x83, 0x00, 0xaa, 0x00, 0x57, 0x7d, 0xa2); // MyFree Codec Filter

HRESULT SamsungFreeCodecFilterConnection::ConnectFilters(IGraphBuilder *pGB, IBaseFilter *pUpFilter, int nOutputPinOrder, IBaseFilter *pDownFilter, int nInputPinOrder, LPSTR pLogFile, BOOL bManual)
{
	
	HRESULT hr = E_FAIL;

	// Get the first output pin of the new source filter. Audio sources 
	// typically have only one output pin, so for most audio cases finding 
	// any output pin is sufficient.

	bool bIsAnySuccess = false;
	IEnumPins *pUpEnumPins = NULL, *pDownEnumPins = NULL;
	IPin *ppinOut = NULL, *pDownstreamPin;
	IPin *ppinIn = NULL, *pUpstreamPin;
	PIN_DIRECTION direction;

	if(FAILED(pUpFilter->EnumPins(&pUpEnumPins))) return hr;

	if(pLogFile == (LPSTR)-1) pLogFile = NULL;

	int nOutputPinCount = 0;
	while(pUpEnumPins->Next(1, &ppinOut, NULL) == S_OK)
	{
		if(FAILED(ppinOut->QueryDirection(&direction)))
		{
			ppinOut->Release(); continue;
		}

		if(direction != PINDIR_OUTPUT)
		{
			ppinOut->Release(); continue;
		}

		if(++nOutputPinCount != nOutputPinOrder)
		{
			if(nOutputPinOrder > 0)
			{
				// When indicating detail number of output pin and connecting
				ppinOut->Release(); continue;
			}
		}

		if(pLogFile)
		{
//			wsprintf(mstr, "Finding (%d)th output pin : ", (short)(nOutputPinCount));
//			CheckPin(ppinOut, &mstr[lstrlen(mstr)], 1024);
//			StringToFile(pLogFile, mstr, 0);
		}

		ppinOut->ConnectedTo(&pDownstreamPin);
		if(pDownstreamPin)
		{
			// If other connection is existed with other pin, disconnect the connection
			if(pLogFile)
			{
//				lstrcpy(mstr, "But other connection is existed  : ");
//				CheckPin(pDownstreamPin, &mstr[lstrlen(mstr)], 1024);
//				StringToFile(pLogFile, mstr, 0);
			}

			hr = pGB->Disconnect(ppinOut); // Disconnect the connection with current pin.
//			hr = ppinOut->Disconnect(); // Disconnect the connection with current pin.

			hr = pGB->Disconnect(pDownstreamPin); // Disconnect the connection with current pin.
//			hr = pDownstreamPin->Disconnect(); // Disconnect the connection with current pin.

			pDownstreamPin->Release();
		}

		if(pDownFilter == NULL)
		{
			//If Down filter is not defined, connect anonymous filters automatically
			hr = pGB->Render(ppinOut);
		}
		else if(SUCCEEDED(pDownFilter->EnumPins(&pDownEnumPins)))
		{
			int nInputPinCount = 0;
			while(pDownEnumPins->Next(1, &ppinIn, 0) == S_OK)
			{
				if(FAILED(ppinIn->QueryDirection(&direction)))
				{
					ppinIn->Release(); continue;
				}

				if(direction != PINDIR_INPUT)
				{
					ppinIn->Release(); continue;
				}

				if(++nInputPinCount != nInputPinOrder)
				{
					if(nInputPinOrder > 0)
					{
						// Connecting input pin with indicating detail number
						ppinIn->Release(); continue;
					}
				}

				if(pLogFile)
				{
//					wsprintf(mstr, "Finding (%d)th input pin : ", (short)(nInputPinCount));
//					CheckPin(ppinIn, &mstr[lstrlen(mstr)], 1024);
//					StringToFile(pLogFile, mstr, 0);
				}

				hr = ppinIn->ConnectedTo(&pUpstreamPin);
				if(pUpstreamPin)
				{
					// If other connection is existed with other pin, disconnect the connection

					if(pLogFile)
					{
//						lstrcpy(mstr, "But other connection is existed  : ");
//						CheckPin(pUpstreamPin, &mstr[lstrlen(mstr)], 1024);
//						StringToFile(pLogFile, mstr, 0);
					}

					hr = pGB->Disconnect(ppinIn); // Disconnect the connection with current pin.
//					hr = ppinIn->Disconnect(); // Disconnect the connection with current pin.

					hr = pGB->Disconnect(pUpstreamPin); // Disconnect the connection with current pin.
//					hr = pUpstreamPin->Disconnect(); // Disconnect the connection with current pin.

					pUpstreamPin->Release();
				}

				if(bManual == FALSE)
				{
					__try {
						hr = pGB->Connect(ppinOut, ppinIn); // Auto
					}
					__except(EXCEPTION_EXECUTE_HANDLER) {
						// When errors are occrured
						hr = E_FAIL;
					}
				}
				else
				{
					__try {
						hr = ppinOut->Connect(ppinIn, NULL); // Manual
					}
					__except(EXCEPTION_EXECUTE_HANDLER) {
						// When errors are occrured
						hr = E_FAIL;
					}
				}

				if(hr == S_OK) {
					bIsAnySuccess = true;

					if(pLogFile)
					{
//						StringToFile(pLogFile, "Connection is succeeded", 0);
					}
				}
				else {
					if (hr == VFW_S_PARTIAL_RENDER)
						bIsAnySuccess = false;
					else if (hr == E_ABORT)
						bIsAnySuccess = false;
					else if (hr == E_POINTER)
						bIsAnySuccess = false;
					else if (hr == VFW_E_CANNOT_CONNECT)
						bIsAnySuccess = false;
					else if (hr == VFW_E_NOT_IN_GRAPH)
						bIsAnySuccess = false;
					else
						bIsAnySuccess = false;

					if(pLogFile)
					{
//						StringToFile(pLogFile, "Connection is failed", 0);
					}
				}
				ppinIn->Release();
				if(bIsAnySuccess) break;
			}
			pDownEnumPins->Release();
		}

		ppinOut->Release();
		if(bIsAnySuccess) break;
	}

	pUpEnumPins->Release();

	return (bIsAnySuccess) ? hr : E_FAIL;
}

HRESULT SamsungFreeCodecFilterConnection::AddFilterByCLSID(IGraphBuilder *pGraph, const GUID &clsid, IBaseFilter **ppF, LPCWSTR wszName)
{
	*ppF = 0;

    IBaseFilter *pFilter = NULL;
    
    HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, 
        IID_PPV_ARGS(&pFilter));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pGraph->AddFilter(pFilter, wszName);
    if (FAILED(hr))
    {
        goto done;
    }

    *ppF = pFilter;
    (*ppF)->AddRef();

done:
	SAFE_RELEASE(pFilter);
    return hr;
}

void SamsungFreeCodecFilterConnection::DeleteFilter(IGraphBuilder *pigb, IBaseFilter *spf, int flag)
{
	HRESULT	hr;
	ULONG fetched;
	PIN_INFO pin_info;
	PIN_DIRECTION pin_dir;
	IEnumPins *sEnumPins;
	BYTE oPinCnt, iPinCnt;
	IPin *sPin, *dPin;

	while(flag)
	{
		if(SUCCEEDED(spf->EnumPins(&sEnumPins)))
		{
			dPin = NULL;
			oPinCnt = iPinCnt = 0;
			sEnumPins->Reset(); // Search from first Pin
			while(SUCCEEDED(sEnumPins->Next(1,&sPin,&fetched)) && fetched)
			{
				hr = sPin->QueryDirection(&pin_dir);
				if(pin_dir == PINDIR_OUTPUT)
				{
					if((oPinCnt++ == 0) && (flag > 0))
					{
						hr = sPin->ConnectedTo(&dPin);

//						if(dPin) hr = pigb->Disconnect(dPin); // Disconnect the connection with current pin
//						hr = pigb->Disconnect(sPin); // Disconnect the connection with current pin
					}
				}
				else if(pin_dir == PINDIR_INPUT)
				{
					if((iPinCnt++ == 0) && (flag < 0))
					{
						hr = sPin->ConnectedTo(&dPin);

//						if(dPin) hr = pigb->Disconnect(dPin); // Disconnect the connection with current pin
//						hr = pigb->Disconnect(sPin); // Disconnect the connection with current pin
					}
				}
				sPin->Release();
			}
			sEnumPins->Release();
		}

		if(flag > 0)
		{
			if(oPinCnt == 0) break;
		}
		else
		{
			if(oPinCnt >= 2)
			{
				if(dPin) dPin->Release();
				return;
			}
			if(iPinCnt == 0) break;
		}

		if(dPin == NULL) break;
		hr = pigb->RemoveFilter(spf);
		hr = dPin->QueryPinInfo(&pin_info);
		spf = pin_info.pFilter;
		dPin->Release();
	}

	pigb->RemoveFilter(spf);
}

int SamsungFreeCodecFilterConnection::RenderOutputPins(IGraphBuilder *pGB, IBaseFilter *pBaseFilter)
{
	int				result;
	IEnumPins*		pEnumPin = NULL;
    IPin*			pConnectedPin = NULL, *pPin = NULL;
    PIN_DIRECTION	PinDirection;
    ULONG			ulFetched;
	BOOL			isConnected = FALSE;
	
	if( pBaseFilter == NULL )
	{
		goto finish;
	}

	//
	// Enumerate all pins on the filter
	//
    	result = pBaseFilter->EnumPins( &pEnumPin );
	if( result != 0 )
	{
		goto finish;
	}

	//
	// Step through every pin, looking for the output pins
	//
	while( pEnumPin->Next( 1L, &pPin, &ulFetched ) == S_OK )
	{
		//
		// Is this pin connected?  We're not interested in connected pins.
		//
		result = pPin->ConnectedTo( &pConnectedPin );
		SAFE_RELEASE( pConnectedPin );

		// If this pin is not connected, render it.
		if( result == VFW_E_NOT_CONNECTED )
		{
			result = pPin->QueryDirection( &PinDirection );
			if ( ( S_OK == result ) && ( PinDirection == PINDIR_OUTPUT ) )
			{
				result = pGB->Render( pPin );
				if( ( result != 0 ) &&
					( result != VFW_S_AUDIO_NOT_RENDERED ) &&
					( result != VFW_S_DUPLICATE_NAME ) &&
					( result != VFW_S_PARTIAL_RENDER ) &&
					( result != VFW_S_VIDEO_NOT_RENDERED ) )
				{
					goto finish;
				}

				isConnected = TRUE;
			}
		}
		
		SAFE_RELEASE( pPin );
	}

	result = 0;

finish:
	if( isConnected == FALSE )
	{

	}
	
	if( result != 0 )
	{

	}

	SAFE_RELEASE( pEnumPin );
	SAFE_RELEASE( pPin );
	SAFE_RELEASE( pConnectedPin );

	return result;
}

void SamsungFreeCodecFilterConnection::CheckFilter(IGraphBuilder *pGB)
{
	if (pGB != NULL)
    {
        IEnumFilters* enumFilter;
        IBaseFilter * pFilter;
        ULONG cFetch;
        pGB->EnumFilters(&enumFilter);
        while (enumFilter->Next(1, &pFilter, &cFetch) == 0)
        {
            FILTER_INFO filterInfo;
            int hri = pFilter->QueryFilterInfo(&filterInfo);

			char temp[1024];			
			WideCharToMultiByte(CP_ACP, 0, filterInfo.achName, sizeof(filterInfo.achName)/sizeof(WCHAR), temp, strlen(temp), '\0', FALSE);
			CString strTemp(temp);
			OutputDebugString((LPCWSTR)strTemp);
        }
    }
}

HRESULT SamsungFreeCodecFilterConnection::Make_MyFree_Render(IGraphBuilder *pigb, 
															 LPWSTR szFile, 
															 LPSTR err_msg,
															 CWnd *pParentWnd, 
															 CRect pRect, 
															 IBaseFilter *pVmr,
															 IVMRWindowlessControl9  *pWC,
															 IVMRFilterConfig9 *pConfig 
															 /*,IBasicAudio   *pAudio*/)
{
	HRESULT		hr;
	int			nPinNo = 0;
	IBaseFilter	*pRd=NULL, *pSp=NULL, *pAd=NULL, *pVd=NULL, *pAr=NULL, *pVr=pVmr;//, *pTr=NULL;
	CString soundRender=L"why god ?";
		
	if(err_msg) {
		// Initialization the buffer saving error messages
		err_msg[0] = 0;
	}

	hr = CoCreateInstance(CLSID_MyFreeSync, NULL, CLSCTX_INPROC, IID_IBaseFilter, (LPVOID *)&pRd);
	if(FAILED(hr)) {
		//if(err_msg) ;//lstrcpy(err_msg, "Cannot find the [MyFree Reader-Sync] Filter");
		goto Exit_Make_MyFree_Filter;
	}
	
	hr = pigb->AddFilter(pRd, L"MyFree Reader-Sync Filter");
	if(FAILED(hr)) 
	{
		goto Exit_Make_MyFree_Filter;
	}
	if(szFile)
	{
		IFileSourceFilter *pSF;
		hr = pRd->QueryInterface(IID_IFileSourceFilter, (void **)&pSF);
		if(FAILED(hr)) {
			//if(err_msg) ;//lstrcpy(err_msg, "Cannot find the [IFileSourceFilter] Interface");
			goto Exit_Make_MyFree_Filter;
		}

		hr = pSF->Load((LPOLESTR)szFile, NULL);
		pSF->Release();

		if(FAILED(hr)) {
			//if(err_msg) ;//lstrcpy(err_msg, "The fail is the IFileSourceFilter->Load()");
			goto Exit_Make_MyFree_Filter;
		}
	}

	//CLSID_AudioRender
	hr = CoCreateInstance(CLSID_DSoundRender, NULL, CLSCTX_INPROC, IID_IBaseFilter, (LPVOID *)&pAr);//CLSID_DSoundRender
	if(FAILED(hr)) {
		//if(err_msg) ;//lstrcpy(err_msg, "Cannot find the [Default DirectSound Device] Filter");
		goto Exit_Make_MyFree_Filter;
	}
	//Speakers (Realtek High Definiti//Default WaveOut Device.//Audio Renderer//Default DirectSound Device //DirectSound:Speakers (Realtek High Definition Audio)//NULL
	soundRender =  GetSoundRenderer();
	//hr = pigb->AddFilter(pAr, soundRender);
	hr = pigb->AddFilter(pAr, L"Default DirectSound Device");
	if(FAILED(hr)) 
	{
		goto Exit_Make_MyFree_Filter;
	}	

	//--------------------------------Default render--------------------------------------------------//
	//hr = CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (LPVOID *)&pVr);
	//if(FAILED(hr)) {
	//	if(err_msg) ;//lstrcpy(err_msg, "Cannot find the [Video Renderer] Filter");
	//	goto Exit_Make_MyFree_Filter;
	//}

	//hr = pigb->AddFilter(pVr, L"Video Renderer"); // //Video Renderer
	//if(FAILED(hr)) 
	//{
	//	goto Exit_Make_MyFree_Filter;
	//}
	//--------------------------------------------------------------------------------------------------//

	//------------------------------VideoMixingRender9---------------------------------------------
	hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC, IID_IBaseFilter, (LPVOID *)&pVr);
	if(FAILED(hr)) {
		//if(err_msg) ;//lstrcpy(err_msg, "Cannot find the [Video Renderer] Filter");
		goto Exit_Make_MyFree_Filter;
	}

	hr = pigb->AddFilter(pVr, L"VideoMixingRender9");
	if(FAILED(hr)) 
	{
		goto Exit_Make_MyFree_Filter;
	}
	pVr->QueryInterface(IID_IVMRFilterConfig9,(void **)&pConfig);
	pConfig->SetRenderingMode(VMR9Mode_Windowless);
	pVr->QueryInterface(IID_IVMRWindowlessControl9,(void**)&pWC);
	pWC->SetVideoPosition(NULL,pRect);
	pWC->SetVideoClippingWindow(pParentWnd->m_hWnd);

	//-----------------------------------------------------------------------------------------------------
	hr = ConnectFilters(pigb, pRd, ++nPinNo, pAr, 1, (LPSTR)-1, FALSE);
	if(FAILED(hr))
	{
		//if(err_msg) ;//lstrcpy(err_msg, "Cannot connect [MyFree Reader-Sync] -> [Default DirectSound Device]");
		DeleteFilter(pigb, pAr, 0);
		pAr = NULL;
		nPinNo--;
		//goto Exit_Make_MyFree_Filter;
	}


	hr = ConnectFilters(pigb, pRd, ++nPinNo, pVr, 1, (LPSTR)-1, FALSE);
	if(FAILED(hr))
	{
		//if(err_msg) ;//lstrcpy(err_msg, "Cannot connect [MyFree Reader-Sync] -> [Video Renderer]");
		DeleteFilter(pigb, pVr, 0);
		pVr = NULL;
		nPinNo--;
	}

Exit_Make_MyFree_Filter :
	
	if(pRd)
	{
		if(nPinNo == 0)
			DeleteFilter(pigb, pRd, 0);
		else
			pRd->Release();
	}
	if(pVr)
	{
		if(nPinNo == 0)
			DeleteFilter(pigb, pVr, 0);
		else
			pVr->Release();
	}
	if(pAr)
	{
		if(nPinNo == 0)
			DeleteFilter(pigb, pAr, 0);
		else {}
			pAr->Release();
	}
	if(pWC)
	{
		SAFE_RELEASE(pWC);
	}
	if(pConfig)
	{
		SAFE_RELEASE(pConfig);
	}
	return hr;
}

CString SamsungFreeCodecFilterConnection::GetSoundRenderer()
{
		//-----------------------Test enumrating sound output device-----------------
	CString  soundRd;
	// Create the System Device Enumerator.
	HRESULT hr;
	ICreateDevEnum *pSysDevEnum = NULL;
	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
		IID_ICreateDevEnum, (void **)&pSysDevEnum);
	if (FAILED(hr))
	{
		return NULL;
	}

	// Obtain a class enumerator for the video compressor category.
	IEnumMoniker *pEnumCat = NULL;
	hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumCat, 0);

	if (hr == S_OK) 
	{
		// Enumerate the monikers.
		IMoniker *pMoniker = NULL;
		ULONG cFetched;
		while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
		{
			IPropertyBag *pPropBag;
			hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, 
				(void **)&pPropBag);
			if (SUCCEEDED(hr))
			{
				// To retrieve the filter's friendly name, do the following:
				VARIANT varName;
				VariantInit(&varName);
				hr = pPropBag->Read(L"FriendlyName", &varName, 0);
				if (SUCCEEDED(hr))
				{
					// Display the name in your UI somehow.
					soundRd = varName; 
				}
				VariantClear(&varName);

				// To create an instance of the filter, do the following:
				IBaseFilter *pFilter;
				hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
					(void**)&pFilter);
				// Now add the filter to the graph. 
				//Remember to release pFilter later.
				pPropBag->Release();
			}
			pMoniker->Release();
			break;
		}
		pEnumCat->Release();
	}
	pSysDevEnum->Release();
	//---------------------------------------------------------------------------
	return soundRd;
}